<?php
/**
 * SimpleMail - 轻量级 SMTP 邮件发送类
 * 支持 SMTP 认证、HTML 邮件和附件
 */
class SimpleMail {
    private $smtpHost = '';       // SMTP 服务器地址
    private $smtpPort = 587;      // SMTP 端口 (默认 587, SSL: 465)
    private $smtpSecure = 'tls';  // 加密方式 (tls/ssl/null)
    private $smtpUser = '';       // SMTP 用户名
    private $smtpPass = '';       // SMTP 密码
    private $smtpTimeout = 30;    // 超时时间 (秒)
    
    private $from = '';           // 发件人邮箱
    private $fromName = '';       // 发件人名称
    private $to = [];             // 收件人列表
    private $cc = [];             // 抄送列表
    private $bcc = [];            // 密送列表
    private $subject = '';        // 邮件主题
    private $body = '';           // 邮件正文
    private $isHtml = false;      // 是否为 HTML 邮件
    private $attachments = [];    // 附件列表
    private $headers = [];        // 自定义头部
    
    private $error = '';          // 错误信息
    
    /**
     * 设置 SMTP 服务器配置
     */
    public function setSmtpConfig($host, $port = 587, $secure = 'tls', $user = '', $pass = '') {
        $this->smtpHost = $host;
        $this->smtpPort = $port;
        $this->smtpSecure = $secure;
        $this->smtpUser = $user;
        $this->smtpPass = $pass;
        return $this;
    }
    
    /**
     * 设置发件人
     */
    public function setFrom($email, $name = '') {
        $this->from = $email;
        $this->fromName = $name;
        return $this;
    }
    
    /**
     * 添加收件人
     */
    public function addTo($email, $name = '') {
        $this->to[] = ['email' => $email, 'name' => $name];
        return $this;
    }
    
    /**
     * 添加抄送人
     */
    public function addCc($email, $name = '') {
        $this->cc[] = ['email' => $email, 'name' => $name];
        return $this;
    }
    
    /**
     * 添加密送人
     */
    public function addBcc($email, $name = '') {
        $this->bcc[] = ['email' => $email, 'name' => $name];
        return $this;
    }
    
    /**
     * 设置邮件主题
     */
    public function setSubject($subject) {
        $this->subject = $subject;
        return $this;
    }
    
    /**
     * 设置邮件正文
     */
    public function setBody($body, $isHtml = false) {
        $this->body = $body;
        $this->isHtml = $isHtml;
        return $this;
    }
    
    /**
     * 添加附件
     */
    public function addAttachment($path, $name = '') {
        if (file_exists($path)) {
            $this->attachments[] = [
                'path' => $path,
                'name' => $name ?: basename($path)
            ];
        } else {
            $this->error = "附件文件不存在: $path";
        }
        return $this;
    }
    
    /**
     * 添加自定义头部
     */
    public function addHeader($key, $value) {
        $this->headers[$key] = $value;
        return $this;
    }
    
    /**
     * 获取错误信息
     */
    public function getError() {
        return $this->error;
    }
    
    /**
     * 发送邮件
     */
    public function send() {
        // 验证配置
        if (empty($this->smtpHost)) {
            $this->error = 'SMTP 服务器地址未设置';
            return false;
        }
        
        if (empty($this->from)) {
            $this->error = '发件人邮箱未设置';
            return false;
        }
        
        if (empty($this->to)) {
            $this->error = '至少需要一个收件人';
            return false;
        }
        
        // 连接 SMTP 服务器
        $socket = $this->connect();
        if (!$socket) {
            return false;
        }
        
        // 发送邮件
        $result = $this->sendEmail($socket);
        
        // 关闭连接
        fwrite($socket, "QUIT\r\n");
        fclose($socket);
        
        return $result;
    }
    
    /**
     * 连接 SMTP 服务器
     */
    private function connect() {
        $host = $this->smtpHost;
        $port = $this->smtpPort;
        
        // 添加安全协议前缀
        if ($this->smtpSecure === 'ssl') {
            $host = 'ssl://' . $host;
        } elseif ($this->smtpSecure === 'tls') {
            // STARTTLS 将在连接后启用
        }
        
        // 创建套接字连接
        $socket = fsockopen($host, $port, $errno, $errstr, $this->smtpTimeout);
        if (!$socket) {
            $this->error = "无法连接到 SMTP 服务器: $errstr ($errno)";
            return false;
        }
        
        // 设置超时
        stream_set_timeout($socket, $this->smtpTimeout);
        
        // 读取服务器响应
        $response = fgets($socket, 512);
        if (substr($response, 0, 3) != '220') {
            $this->error = "SMTP 服务器连接失败: $response";
            fclose($socket);
            return false;
        }
        
        // 发送 HELO/EHLO
        if (!$this->sendCommand($socket, "EHLO localhost", '250')) {
            // 尝试 HELO
            if (!$this->sendCommand($socket, "HELO localhost", '250')) {
                $this->error = "SMTP 服务器不支持 EHLO/HELO";
                fclose($socket);
                return false;
            }
        }
        
        // 启用 TLS
        if ($this->smtpSecure === 'tls') {
            if (!$this->sendCommand($socket, "STARTTLS", '220')) {
                $this->error = "无法启用 TLS 加密";
                fclose($socket);
                return false;
            }
            
            // 启用加密
            stream_socket_enable_crypto($socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
            
            // 重新发送 EHLO
            if (!$this->sendCommand($socket, "EHLO localhost", '250')) {
                $this->error = "TLS 加密后无法重新发送 EHLO";
                fclose($socket);
                return false;
            }
        }
        
        // 验证登录
        if (!empty($this->smtpUser) && !empty($this->smtpPass)) {
            if (!$this->sendCommand($socket, "AUTH LOGIN", '334')) {
                // 尝试 PLAIN 认证
                if (!$this->sendCommand($socket, "AUTH PLAIN", '334')) {
                    $this->error = "SMTP 服务器不支持认证";
                    fclose($socket);
                    return false;
                }
            }
            
            // 发送用户名和密码
            if (!$this->sendCommand($socket, base64_encode($this->smtpUser), '334')) {
                $this->error = "认证失败: 无效的用户名";
                fclose($socket);
                return false;
            }
            
            if (!$this->sendCommand($socket, base64_encode($this->smtpPass), '235')) {
                $this->error = "认证失败: 无效的密码";
                fclose($socket);
                return false;
            }
        }
        
        return $socket;
    }
    
    /**
     * 发送邮件内容
     */
    private function sendEmail($socket) {
        // 发送 MAIL FROM
        $from = $this->formatAddress($this->from, $this->fromName);
        if (!$this->sendCommand($socket, "MAIL FROM:<{$this->from}>", '250')) {
            $this->error = "发送 MAIL FROM 命令失败";
            return false;
        }
        
        // 发送 RCPT TO (收件人)
        foreach ($this->to as $recipient) {
            if (!$this->sendCommand($socket, "RCPT TO:<{$recipient['email']}>", '250,251')) {
                $this->error = "添加收件人失败: {$recipient['email']}";
                return false;
            }
        }
        
        // 发送 RCPT TO (抄送)
        foreach ($this->cc as $recipient) {
            if (!$this->sendCommand($socket, "RCPT TO:<{$recipient['email']}>", '250,251')) {
                $this->error = "添加抄送人失败: {$recipient['email']}";
                return false;
            }
        }
        
        // 发送 RCPT TO (密送)
        foreach ($this->bcc as $recipient) {
            if (!$this->sendCommand($socket, "RCPT TO:<{$recipient['email']}>", '250,251')) {
                $this->error = "添加密送人失败: {$recipient['email']}";
                return false;
            }
        }
        
        // 发送 DATA
        if (!$this->sendCommand($socket, "DATA", '354')) {
            $this->error = "发送 DATA 命令失败";
            return false;
        }
        
        // 构建邮件头部
        $headers = $this->buildHeaders();
        
        // 构建邮件正文
        $body = $this->buildBody();
        
        // 发送邮件内容
        fwrite($socket, $headers . "\r\n" . $body . "\r\n.\r\n");
        
        // 检查响应
        $response = fgets($socket, 512);
        if (substr($response, 0, 3) != '250') {
            $this->error = "发送邮件内容失败: $response";
            return false;
        }
        
        return true;
    }
    
    /**
     * 构建邮件头部
     */
    private function buildHeaders() {
        $headers = [];
        
        // 发件人
        $from = $this->formatAddress($this->from, $this->fromName);
        $headers[] = "From: $from";
        
        // 收件人
        if (!empty($this->to)) {
            $toAddresses = [];
            foreach ($this->to as $recipient) {
                $toAddresses[] = $this->formatAddress($recipient['email'], $recipient['name']);
            }
            $headers[] = "To: " . implode(', ', $toAddresses);
        }
        
        // 抄送
        if (!empty($this->cc)) {
            $ccAddresses = [];
            foreach ($this->cc as $recipient) {
                $ccAddresses[] = $this->formatAddress($recipient['email'], $recipient['name']);
            }
            $headers[] = "Cc: " . implode(', ', $ccAddresses);
        }
        
        // 邮件主题
        $headers[] = "Subject: " . $this->encodeHeader($this->subject);
        
        // 日期
        $headers[] = "Date: " . date('r');
        
        // 消息 ID
        $headers[] = "Message-ID: <" . md5(uniqid(time())) . "@" . $_SERVER['SERVER_NAME'] . ">";
        
        // MIME 版本
        $headers[] = "MIME-Version: 1.0";
        
        // 内容类型
        if (!empty($this->attachments)) {
            // 带附件的邮件
            $boundary = "=_boundary_" . md5(uniqid(time()));
            $headers[] = "Content-Type: multipart/mixed; boundary=\"$boundary\"";
            $this->boundary = $boundary;
        } elseif ($this->isHtml) {
            // HTML 邮件
            $headers[] = "Content-Type: text/html; charset=\"utf-8\"";
            $headers[] = "Content-Transfer-Encoding: base64";
        } else {
            // 纯文本邮件
            $headers[] = "Content-Type: text/plain; charset=\"utf-8\"";
            $headers[] = "Content-Transfer-Encoding: base64";
        }
        
        // 添加自定义头部
        foreach ($this->headers as $key => $value) {
            $headers[] = "$key: $value";
        }
        
        return implode("\r\n", $headers);
    }
    
    /**
     * 构建邮件正文
     */
    private function buildBody() {
        if (!empty($this->attachments)) {
            // 带附件的邮件
            $body = "--{$this->boundary}\r\n";
            
            if ($this->isHtml) {
                $body .= "Content-Type: text/html; charset=\"utf-8\"\r\n";
            } else {
                $body .= "Content-Type: text/plain; charset=\"utf-8\"\r\n";
            }
            
            $body .= "Content-Transfer-Encoding: base64\r\n\r\n";
            $body .= chunk_split(base64_encode($this->body)) . "\r\n";
            
            // 添加附件
            foreach ($this->attachments as $attachment) {
                $body .= "--{$this->boundary}\r\n";
                $body .= "Content-Type: application/octet-stream; name=\"{$this->encodeHeader($attachment['name'])}\"\r\n";
                $body .= "Content-Transfer-Encoding: base64\r\n";
                $body .= "Content-Disposition: attachment; filename=\"{$this->encodeHeader($attachment['name'])}\"\r\n\r\n";
                
                $fileContent = file_get_contents($attachment['path']);
                $body .= chunk_split(base64_encode($fileContent)) . "\r\n";
            }
            
            $body .= "--{$this->boundary}--";
            return $body;
        } else {
            // 无附件的邮件
            return chunk_split(base64_encode($this->body));
        }
    }
    
    /**
     * 发送 SMTP 命令并检查响应
     */
    private function sendCommand($socket, $command, $expectedResponse) {
        fwrite($socket, "$command\r\n");
        
        $response = '';
        while ($line = fgets($socket, 512)) {
            $response .= $line;
            if (substr($line, 3, 1) != '-') {
                break;
            }
        }
        
        $expectedResponses = explode(',', $expectedResponse);
        foreach ($expectedResponses as $code) {
            if (substr($response, 0, 3) == trim($code)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 格式化邮件地址
     */
    private function formatAddress($email, $name = '') {
        if (empty($name)) {
            return $email;
        }
        
        return "\"{$this->encodeHeader($name)}\" <$email>";
    }
    
    /**
     * 编码非 ASCII 字符的邮件头部
     */
    private function encodeHeader($text) {
        if (preg_match('/[\x80-\xFF]/', $text)) {
            return '=?UTF-8?B?' . base64_encode($text) . '?=';
        }
        return $text;
    }
}
?>